using System;
using System.Diagnostics.Contracts;

namespace OpenWaves
{
    public class AbsoluteUrlBuilder : UrlBuilder
    {
        public const int DefaultPort = 0;

        private string host;
        private int port;
        private string scheme = Uri.UriSchemeHttp;

        public string Scheme
        {
            get { return this.scheme; }
            set
            {
                Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(value) == false);
                Contract.Requires<ArgumentException>(Uri.CheckSchemeName(value));

                this.scheme = value;
            }
        }

        public string Host
        {
            get { return this.host; }
            set
            {
                Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(value) == false);
                this.host = value;
            }
        }

        public int Port
        {
            get { return this.port; }
            set
            {
                //port can be -1 if uri class cannot determine default port for 
                Contract.Requires<ArgumentException>(value >= -1, "Port must be non-negative or -1 for unknown default port.");
                this.port = value;
            }
        }

        public new AbsoluteUrl ToUrl()
        {
            Contract.Ensures(Contract.Result<AbsoluteUrl>() != null);
            return this.BuildAbsoluteUrl();
        }

        public static AbsoluteUrlBuilder From(AbsoluteUrl url)
        {
            Contract.Requires<ArgumentNullException>(url != null);
            Contract.Ensures(Contract.Result<AbsoluteUrlBuilder>() != null);

            var builder = new AbsoluteUrlBuilder
                       {
                           Scheme = url.Scheme,
                           Fragment = url.Fragment,
                           Host = url.Host,
                           Path = url.Path,
                           Port = url.Port
                       };

            foreach (var parameter in url.Query)
            {
                builder.AddParameter(parameter.Key, parameter.Value);
            }

            return builder;
        }

        protected override Url BuildUrl()
        {
            return this.BuildAbsoluteUrl();
        }

        private AbsoluteUrl BuildAbsoluteUrl()
        {
            var uriBuilder = new UriBuilder
            {
                Scheme = this.Scheme,
                Host = this.Host,
                Path = this.Path.ToString(),
                Query = UrlQueryCollection.FromNameValueDictionary(this.Query).ToString(),
                Fragment = this.Fragment
            };

            if (this.Port > 0)
                uriBuilder.Port = this.Port;

            return new AbsoluteUrl(uriBuilder.Uri);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
        public static AbsoluteUrlBuilder Create(string scheme, string host, RelativeUrl url)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(scheme) == false);
            Contract.Requires<ArgumentNullException>(Uri.CheckSchemeName(scheme));
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(host) == false);
            Contract.Requires<ArgumentNullException>(url != null);

            Contract.Ensures(Contract.Result<AbsoluteUrlBuilder>() != null);

            var builder = new AbsoluteUrlBuilder
                              {
                                 Scheme = scheme,
                                 Fragment = url.Fragment,
                                 Host = host,
                                 Path = url.Path
                             };
            
            foreach (var parameter in url.Query)
            {
                builder.AddParameter(parameter.Key, parameter.Value);
            }

            return builder;
        }
    }
}
